package cn.virens.web.controller;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.virens.Assert;
import cn.virens.common.CloseableUtil;
import cn.virens.database.mysql.model.base.BaseUploadImage;
import cn.virens.exception.ExceptionUtil;
import cn.virens.service.inter.base.upload.BaseUploadImageService;
import cn.virens.web.common.BaseJSONController;
import cn.virens.web.components.spring.exception.NotFileFoundException;

@RestController
public class UploadImageContorller extends BaseJSONController {
	private final static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");

	private @Autowired BaseUploadImageService mBaseImageService;

	@RequestMapping(path = "/down/image/{code}")
	public void down(@PathVariable String code, HttpServletRequest request, HttpServletResponse response) throws IOException {
		OutputStream outputStream = null;
		InputStream inputStream = null;

		try {
			BaseUploadImage file = mBaseImageService.selectOne(code);
			if (file == null) { throw new NotFileFoundException("Image", code); }

			// 将文件传输到输出流
			File file2 = FileUtil.file(file.getPath(getRootPath(request)));
			if (!file2.exists()) { throw new NotFileFoundException("Image", code); }

			// 填充 响应头
			response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=180");
			response.setHeader(HttpHeaders.CONTENT_TYPE, file.contentType());
			response.setHeader(HttpHeaders.CONTENT_LENGTH, file.contentLength());

			// 获取输入*输出流
			inputStream = FileUtil.getInputStream(file2);
			outputStream = response.getOutputStream();

			// 文件流输出
			int len = 0;
			byte[] buffer = new byte[1024];
			while ((len = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, len);
			}
		} catch (NotFileFoundException e) {
			response.sendError(404, e.getMessage());
		} catch (IOException e) {
			response.sendError(500, e.getMessage());
		} finally {
			CloseableUtil.close(inputStream);
			CloseableUtil.close(outputStream);
		}
	}

	@RequestMapping(path = "/down/thumb/{code}")
	public void thumb(@PathVariable String code, HttpServletRequest request, HttpServletResponse response) throws IOException {
		OutputStream outputStream = null;
		InputStream inputStream = null;

		try {
			BaseUploadImage file = mBaseImageService.selectOne(code);
			if (file == null) { throw new NotFileFoundException("Image", code); }

			// 将文件传输到输出流
			File file2 = FileUtil.file(file.getThumb(getRootPath(request)));
			if (!file2.exists()) { throw new NotFileFoundException("Image", code); }

			// 填充 响应头
			response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=180");
			response.setHeader(HttpHeaders.CONTENT_TYPE, "image/jpeg");

			// 获取输入*输出流
			inputStream = FileUtil.getInputStream(file2);
			outputStream = response.getOutputStream();

			// 文件流输出
			int len = 0;
			byte[] buffer = new byte[1024];
			while ((len = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, len);
			}
		} catch (NotFileFoundException e) {
			response.sendError(404, e.getMessage());
		} catch (IOException e) {
			response.sendError(500, e.getMessage());
		} finally {
			CloseableUtil.close(inputStream);
			CloseableUtil.close(outputStream);
		}
	}

	@RequestMapping(path = "/upload/image/*.json", method = { RequestMethod.POST, RequestMethod.OPTIONS })
	public void uploadOptions(HttpServletRequest request, HttpServletResponse response, Model model) {
		String origin = StrUtil.emptyToDefault(request.getHeader(HttpHeaders.ORIGIN), "*");

		response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,OPTIONS");
		response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
		response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
		response.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "86400");
	}

	@RequestMapping(path = "/upload/image/base.json", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Model uploadByBase(@RequestParam("file") MultipartFile file, HttpServletRequest request, HttpServletResponse response, Model model) {
		this.uploadOptions(request, response, model);

		try {
			Assert.isNull(file, "文件不存在");

			// 获取存储目录
			String rootPath = getRootPath(request);
			String savePath = getRealPath(request);

			// 获取文件名 & 文件指纹签名
			String fileName = file.getOriginalFilename();
			String fileSign = DigestUtil.md5Hex(file.getBytes());

			// 检查是否已有该文件
			BaseUploadImage baseFile = mBaseImageService.selectOne(fileSign);
			if (baseFile == null || !FileUtil.exist(baseFile.getPath(rootPath))) {
				if (baseFile != null) {
					mBaseImageService.delete(rootPath, fileSign);
				}

				// 构建文件信息对象
				baseFile = new BaseUploadImage();
				baseFile.setCode(fileSign);
				baseFile.setName(fileName);
				baseFile.setLength(file.getSize());
				baseFile.setCreateTime(new Date());
				baseFile.setPath(savePath + fileSign);
				baseFile.setThumb0(savePath + fileSign);
				baseFile.setMimeType(file.getContentType());

				// 原始图片
				File saveFile = FileUtil.touch(baseFile.getPath(rootPath));

				// 保存 & 压缩图片
				saveImage(file.getInputStream(), saveFile);
				scaleImage(baseFile.getThumb(rootPath), saveFile);

				// 保存图片信息
				if (mBaseImageService.insert(baseFile) != null) {
					return renderSuccess(baseFile.getCode(), model, "上传成功");
				} else {
					return renderFail(model, "SaveErr", "保存文件信息失败");
				}
			} else {
				return renderSuccess(baseFile.getCode(), model, "上传成功");
			}
		} catch (Exception e) {
			return renderFail(model, ExceptionUtil.getCode(e), ExceptionUtil.getMessage(e));
		}
	}

	@RequestMapping(path = "/upload/image/layui.json", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Model uploadByLayui(@RequestParam("file") MultipartFile file, HttpServletRequest request, HttpServletResponse response, Model model) {
		this.uploadOptions(request, response, model);

		try {
			Assert.isNull(file, "文件不存在");

			// 获取存储目录
			String rootPath = getRootPath(request);
			String savePath = getRealPath(request);

			// 获取文件名 & 文件指纹签名
			String fileName = file.getOriginalFilename();
			String fileSign = DigestUtil.md5Hex(file.getBytes());

			// 检查是否已有该文件
			BaseUploadImage baseFile = mBaseImageService.selectOne(fileSign);
			if (baseFile == null || !FileUtil.exist(baseFile.getPath(rootPath))) {
				if (baseFile != null) {
					mBaseImageService.delete(rootPath, fileSign);
				}

				// 构建文件信息对象
				baseFile = new BaseUploadImage();
				baseFile.setCode(fileSign);
				baseFile.setName(fileName);
				baseFile.setLength(file.getSize());
				baseFile.setCreateTime(new Date());
				baseFile.setPath(savePath + fileSign);
				baseFile.setThumb0(savePath + fileSign);
				baseFile.setMimeType(file.getContentType());

				// 原始图片
				File saveFile = FileUtil.touch(baseFile.getPath(rootPath));

				// 保存 & 压缩图片
				saveImage(file.getInputStream(), saveFile);
				scaleImage(baseFile.getThumb(rootPath), saveFile);

				// 保存图片信息
				if (mBaseImageService.insert(baseFile) != null) {
					return success(model, baseFile);
				} else {
					return error(model, "保存文件信息失败");
				}
			} else {
				return success(model, baseFile);
			}
		} catch (Exception e) {
			return error(model, ExceptionUtil.getMessage(e));
		}
	}

	/** LayUI专用 */
	private static Model error(Model model, String msg) {
		model.addAttribute("code", "1");
		model.addAttribute("msg", msg);

		return model;
	}

	/** LayUI专用 */
	private static Model success(Model model, BaseUploadImage baseFile) {
		Map<String, Object> data = new HashMap<>();
		data.put("src", "/down/thumb/" + baseFile.getCode());
		data.put("title", baseFile.getName());

		model.addAttribute("code", "0");
		model.addAttribute("data", data);
		model.addAttribute("msg", "上传成功");

		return model;
	}

	private static String getRootPath(HttpServletRequest request) {
		return request.getServletContext().getRealPath("/");
	}

	private static String getRealPath(HttpServletRequest request) {
		return "/upload/images/" + df.format(new Date()) + "/";
	}

	private static void scaleImage(String thumbPath, File imgFile) {
		ImgUtil.scale(imgFile, FileUtil.touch(thumbPath), 800, 600, null);
	}

	private static void saveImage(InputStream in, File imgFile) throws IOException {
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			inputStream = new BufferedInputStream(in);
			outputStream = FileUtil.getOutputStream(imgFile);

			// 保存图片文件
			int temp = 0;
			while ((temp = inputStream.read()) != -1) {
				outputStream.write(temp);
			}
		} finally {
			CloseableUtil.close(inputStream);
			CloseableUtil.close(outputStream);
		}
	}
}
